#include "fxcc/core/graph/common/MeshData.h"

using namespace fxcc::graph::common;

MeshData::MeshData(aiMesh *mesh)
{
    m_Bones = std::make_shared<fxcc::graph::common::Bones>();
    m_GeoDataPtr = std::make_shared<fxcc::graph::common::GeometryData>();
    m_SkinnedMeshDataPtr = std::make_shared<fxcc::graph::common::SkinnedMeshData>();

    m_AABB = AABB(mesh->mAABB);
    m_MaterialIndex = mesh->mMaterialIndex;
    Load(mesh->mName, m_Name);

    for (int boneId = 0; boneId < mesh->mNumBones; boneId++)
    {
        auto newBone = fxcc::graph::common::Bone(mesh->mBones[boneId]);
        newBone.m_Index = boneId;
        m_Bones->m_Bones[newBone.m_Name] = newBone;
    }

    LoadData(mesh);


}

bool MeshData::LoadData(aiMesh *mesh)
{
    auto& m_GeoData = *m_GeoDataPtr;
    auto& m_SkinnedMeshData = *m_SkinnedMeshDataPtr;;

    if (mesh->mNumVertices > 0)
    {
        std::vector<unsigned int> &m_Indices = m_GeoData.indices32;

        for (int i = 0; i < mesh->mNumFaces; i++)
        {
            auto face = mesh->mFaces[i];
            for (int j = 0; j < face.mNumIndices; j++)
            {
                m_Indices.push_back(face.mIndices[j]);
            }
        }

        m_GeoData.ResizeVertices(mesh->mNumVertices);
        memcpy(&m_GeoData.vertices[0], mesh->mVertices, sizeof(glm::vec3) * mesh->mNumVertices);
        memcpy(&m_GeoData.normals[0], mesh->mNormals, sizeof(glm::vec3) * mesh->mNumVertices);

        for (int i = 0; i < mesh->mNumVertices; i++)
        {
            glm::vec2 &texCoord = m_GeoData.texcoords[i];
            texCoord.x = mesh->mTextureCoords[0][i].x;
            texCoord.y = mesh->mTextureCoords[0][i].y;
        }
        if (mesh->HasTangentsAndBitangents())
        {
            memcpy(&m_GeoData.tangents[0], mesh->mTangents, sizeof(glm::vec3) * mesh->mNumVertices);
            memcpy(&m_GeoData.bigTangents[0], mesh->mBitangents, sizeof(glm::vec3) * mesh->mNumVertices);
        }

        if (HasBones())
        {
            m_SkinnedMeshData.Resize(mesh->mNumVertices);
           
            for (auto it : m_Bones->m_Bones)
            {
                std::string name = it.first;
                auto bone = it.second;

                int boneIndex = bone.m_Index;

                for (const auto &weight : bone.m_Weights)
                {
                    int vertexId = weight.m_VertexId;
                    float weightValue = weight.m_Weight;
                    m_SkinnedMeshData.AddBoneWeight(vertexId, boneIndex, weightValue);
                }
            }
        }
    }
    return true;
}

bool MeshData::HasBones() const
{
    return m_Bones->HasBones();
}

std::vector<unsigned int> fxcc::graph::common::MeshData::GetIndices()
{
    auto& m_GeoData = *m_GeoDataPtr;
    auto& m_SkinnedMeshData = *m_SkinnedMeshDataPtr;;
    return m_GeoData.indices32;
};

std::vector<VertexFixed> fxcc::graph::common::MeshData::GetVertexFixed()
{
    auto& m_GeoData = *m_GeoDataPtr;
    auto& m_SkinnedMeshData = *m_SkinnedMeshDataPtr;;

    int numVertices = m_GeoData.vertices.size();
    std::vector<VertexFixed> res;
    if (numVertices)
    {
        res.resize(numVertices);

        for (int i = 0; i < m_GeoData.vertices.size(); i++)
        {
            auto &v = res[i];
            v.pos = m_GeoData.vertices[i];
            v.normal = m_GeoData.normals[i];
            v.tex = m_GeoData.texcoords[i];
            v.bigTangent = m_GeoData.bigTangents[i];
            v.tangent = m_GeoData.tangents[i];
        }
    }
    return res;
};

std::vector<VertexSkinned> fxcc::graph::common::MeshData::GetVertexSkinned()
{
    auto& m_GeoData = *m_GeoDataPtr;
    auto& m_SkinnedMeshData = *m_SkinnedMeshDataPtr;

    int numVertices = m_GeoData.vertices.size();
    std::vector<VertexSkinned> res;
    if (numVertices && HasBones())
    {
        res.resize(numVertices);

        for (int i = 0; i < m_GeoData.vertices.size(); i++)
        {
            auto &v = res[i];
            v.pos = m_GeoData.vertices[i];
            v.normal = m_GeoData.normals[i];
            v.tex = m_GeoData.texcoords[i];
            v.bigTangent = m_GeoData.bigTangents[i];
            v.tangent = m_GeoData.tangents[i];

            v.bones1 = m_SkinnedMeshData.bones1s[i];
            v.weights1 = m_SkinnedMeshData.weights1s[i];

            v.bones2 = m_SkinnedMeshData.bones2s[i];
            v.weights2 = m_SkinnedMeshData.weights2s[i];
        }
    }
    return res;
}